#!/bin/sh

# Constants
AWK_BIN="/usr/bin/awk"
DOCKER_BIN="/opt/docker/bin/docker"
GREP_BIN="/bin/grep"
RM_BIN="/bin/rm"
TAR_BIN="/bin/tar"

# Variables
BACKUP_FILE="-"
CID=""

# Get parameter from inspect file
inspect_get_par()
{
        $AWK_BIN -v PAR="$1" -v SECTION="$2" -v DEPTH="$3" ' \
        BEGIN {depth = 0; search = 0} \
        /[[{]/ {++depth} \
        /[]}]/ {--depth} \
        search == 1 && $1 == ("\"" PAR "\":") {gsub(",.*", "", $2); gsub("\"", "", $2); print $2; exit} \
        SECTION == "" && depth == 1 {search = 1} \
        SECTION != "" && depth == DEPTH && $1 == ("\"" SECTION "\":") {search = 1} \
        ' <$INSPECT_FILE
}

# Get volumes from inspect file
inspect_get_volumes()
{
        inspect_get_section Mounts 3 | \
        $AWK_BIN -F\" ' \
        $2 == "Type" {type = $4} \
        $2 == "Name" {name = $4} \
        $2 == "Source" {if (type == "volume") printf("%s %s\n", name, $4)} \
        '
}

# Get section from inspect file
inspect_get_section()
{
        $AWK_BIN -F\" -v SECTION="$1" -v DEPTH="$2" ' \
        BEGIN {depth = 0; search = 0} \
        /[[{]/ {++depth} \
        /[]}]/ {--depth; if (search == 1 && depth < DEPTH) exit} \
        search == 1 {print} \
        depth == DEPTH && $2 == SECTION {search = 1} \
        ' <$INSPECT_FILE
}

# Get port mapping from inspect file
inspect_get_ports()
{
        inspect_get_section PortBindings 4 | \
        $AWK_BIN -F\" ' \
        $3 == ": [" {cport = $2} \
        $2 == "HostIp" {addr = $4} \
        $2 == "HostPort" {printf("%s %s %s\n", cport, $4, addr)} \
        '
}

# Get mounts from inspect file
inspect_get_mounts()
{
        inspect_get_section Mounts 3 | \
        $AWK_BIN -F\" ' \
        $2 == "Type" {type = $4} \
        $2 == "Name" {name = $4} \
        $2 == "Source" {src = $4} \
        $2 == "Destination" {dst = $4} \
        $2 == "Mode" {printf("%s %s %s\n", type == "volume" ? name : src, dst, $4 == "ro" ? "ro" : "")} \
        '
}

# Get environment from inspect file
inspect_get_env()
{
        inspect_get_section Env 4 | \
        $AWK_BIN -F\" ' \
        $2 ~ "=" {sub("=", " ", $2); printf("%s\n", $2)} \
        '
}

# Get devices from inspect file
inspect_get_devices()
{
        inspect_get_section Devices 4 | \
        $AWK_BIN -F\" ' \
        $2 == "PathOnHost" {poh = $4} \
        $2 == "PathInContainer" {pic = $4} \
        $2 == "CgroupPermissions" {printf("%s %s %s\n", poh, pic, $4)} \
        '
}

# Get arguments from inspect file
inspect_get_args()
{
        inspect_get_section Cmd 4 | \
        $AWK_BIN -F\" ' \
        {printf("%s\n", $2)} \
        '
}

# Backup volume
backup_volume()
{
        VNAME=$1
        VPATH=$2

        /bin/ln -sf ${VPATH/$VNAME*/$VNAME} $BACKUP_DIR/$VNAME

        echo "$DOCKER_BIN volume rm $VNAME 2>/dev/null"
        echo "$DOCKER_BIN volume create $VNAME"
        echo "VPATH=\`$DOCKER_BIN volume inspect $VNAME | $AWK_BIN -F\\\" '\$2 == \"Mountpoint\" {print \$4}'\`"
        echo "$TAR_BIN -xzf \$1 -C \${VPATH/\\/$VNAME*/} $VNAME"
}

# Create 'docker run' command
create_docker_run_cmd()
{
        echo -n "$DOCKER_BIN run -t"

        CONT_NAME=`inspect_get_par Name`
        CONT_NAME=${CONT_NAME#/}
        if [ -n "$CONT_NAME" ]; then
                echo -n " --name $CONT_NAME"
        fi

        RESTART_POLICY=`inspect_get_par Name RestartPolicy 4`
        if [ -n "$RESTART_POLICY" ]; then
                echo -n " --restart $RESTART_POLICY"
        fi

        DETACH=`inspect_get_par AttachStdout Config 3`
        if [ "$DETACH" = "false" ]; then
                echo -n " -d"
        fi

        INTERACTIVE=`inspect_get_par OpenStdin Config 3`
        if [ "$INTERACTIVE" = "true" ]; then
                echo -n " -i"
        fi

        AUTO_REMOVE=`inspect_get_par AutoRemove HostConfig 3`
        if [ "$AUTO_REMOVE" = "true" ]; then
                echo -n " --rm"
        fi

        PRIVILEGED=`inspect_get_par Privileged HostConfig 3`
        if [ "$PRIVILEGED" = "true" ]; then
                echo -n " --privileged"
        fi

        HOST_NAME=`inspect_get_par Hostname Config 3`
        if [ -n "$HOST_NAME" ]; then
                echo -n " -h $HOST_NAME"
        fi

        WORK_DIR=`inspect_get_par WorkingDir Config 3`
        if [ -n "$WORK_DIR" ]; then
                echo -n " -w $WORK_DIR"
        fi

        USER_GROUP=`inspect_get_par User Config 3`
        if [ -n "$USER_GROUP" ]; then
                echo -n " -u $USER_GROUP"
        fi

        MEMORY=`inspect_get_par Memory HostConfig 3`
        if [ "$MEMORY" != "0" ]; then
                echo -n " -m $MEMORY"
        fi

        NANO_CPUS=`inspect_get_par NanoCpus HostConfig 3`
        if [ "$NANO_CPUS" != "0" ]; then
                NANO_CPUS=`echo $NANO_CPUS | $AWK_BIN '{printf("%4.2f", $1/1000000000)}'`
                echo -n " --cpus $NANO_CPUS"
        fi

        inspect_get_ports | while read CPORT HPORT ADDR; do
                if [ -n "$ADDR" ]; then
                        echo $ADDR | $GREP_BIN -q ":"
                        if [ $? -eq 0 ]; then
                                ADDR="[$ADDR]:"
                        else
                                ADDR="$ADDR:"
                        fi
                fi
                echo -n " -p $ADDR$HPORT:$CPORT"
        done

        inspect_get_mounts | while read SRC DST FLG; do
                if [ -n "$FLG" ]; then
                        FLG=":$FLG"
                fi
                echo -n " -v $SRC:$DST$FLG"
        done

        inspect_get_env | while read NAME VALUE; do
                echo -n " -e $NAME=\"$VALUE\""
        done

        inspect_get_devices | while read POH PIC PERM; do
                echo -n " --device $POH:$PIC:$PERM"
        done

        IMAGE_NAME=`inspect_get_par Image Config 3`
        echo -n " $IMAGE_NAME"

        inspect_get_args | while read ARG; do
                echo -n " \"$ARG\""
        done

        echo
}

# Parse options
while [ -n "$1" ]; do
        case "$1" in
        -f)
                shift
                BACKUP_FILE="$1"
                shift
                ;;
        *)
                CID="$1"
                shift $#
                ;;
        esac
done

# Verify that container ID was specified
if [ -z "$CID" ]; then
        echo "Usage: $0 [-f <backup_file>] <Container_ID>"
        exit 1
fi

# Verify that backup_file was specified
if [ -z "$BACKUP_FILE" ]; then
        echo "error: invalid backup file"
        exit 2
fi

# Verify that backup file doesn't exist
if [ -e "$BACKUP_FILE" ]; then
        echo "error: backup file exists yet"
        exit 3
fi

# Create inspect file
INSPECT_FILE="/var/tmp/inspect-$CID"
$DOCKER_BIN inspect $CID >$INSPECT_FILE
if [ $? -ne 0 ]; then
        echo "error: cannot get 'inspect' result"
        exit 4
fi

# Create backup directory
BACKUP_DIR="/var/tmp/$CID"
$RM_BIN -rf $BACKUP_DIR
/bin/mkdir -p $BACKUP_DIR

# Create 'restore' script
RESTORE_SCRIPT="$BACKUP_DIR/restore"
echo "#!/bin/sh" >$RESTORE_SCRIPT
/bin/chmod a+x $RESTORE_SCRIPT

# Create commands for backup of volumes
inspect_get_volumes | while read VNAME VPATH; do
        backup_volume $VNAME $VPATH >>$RESTORE_SCRIPT
done

# Create 'docker run' command
create_docker_run_cmd >>$RESTORE_SCRIPT

# Pause container during backup
$DOCKER_BIN ps -q | $GREP_BIN -q $CID
if [ $? -eq 0 ]; then
        $DOCKER_BIN pause $CID >/dev/null
        PAUSED=1
else
        PAUSED=0
fi

# Create final tar backup file
cd $BACKUP_DIR && \
$TAR_BIN -chzf $BACKUP_FILE *
RETVAL=$?

# Unpause container during backup
if [ $PAUSED -eq 1 ]; then
        $DOCKER_BIN unpause $CID >/dev/null
fi

# Clean up
$RM_BIN -rf $BACKUP_DIR
$RM_BIN -f $INSPECT_FILE

exit $RETVAL
